package com.tianhai.db.index;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * B+树节点
 * 支持序列化到8KB页中
 */
public class BPlusTreeNode {
    
    // 节点类型
    public enum NodeType {
        LEAF,      // 叶子节点
        INTERNAL   // 内部节点
    }
    
    private NodeType nodeType;
    private int pageId;           // 节点对应的页ID
    private boolean isRoot;       // 是否是根节点
    
    // 键值对（整数键）
    private List<Integer> keys;
    
    // 叶子节点：值是页ID
    // 内部节点：值是子节点的页ID
    private List<Integer> values;
    
    // 叶子节点的链接（用于范围查询）
    private int nextLeafPageId;   // 下一个叶子节点的页ID，-1表示没有
    
    // B+树的阶数（每个节点最多的键数）
    private int order;
    
    /**
     * 创建新节点
     */
    public BPlusTreeNode(int pageId, NodeType nodeType, int order) {
        this.pageId = pageId;
        this.nodeType = nodeType;
        this.order = order;
        this.isRoot = false;
        this.keys = new ArrayList<>();
        this.values = new ArrayList<>();
        this.nextLeafPageId = -1;
    }
    
    /**
     * 从字节数组反序列化节点
     */
    public static BPlusTreeNode deserialize(byte[] data, int pageId, int order) {
        ByteBuffer buffer = ByteBuffer.wrap(data);
        
        // 读取节点类型
        int typeValue = buffer.getInt();
        NodeType nodeType = typeValue == 0 ? NodeType.LEAF : NodeType.INTERNAL;
        
        BPlusTreeNode node = new BPlusTreeNode(pageId, nodeType, order);
        
        // 读取是否是根节点
        node.isRoot = buffer.getInt() == 1;
        
        // 读取键的数量
        int keyCount = buffer.getInt();
        
        // 读取下一个叶子节点ID（仅叶子节点有效）
        node.nextLeafPageId = buffer.getInt();
        
        // 读取键
        for (int i = 0; i < keyCount; i++) {
            node.keys.add(buffer.getInt());
        }
        
        // 读取值的数量
        int valueCount = buffer.getInt();
        
        // 读取值
        for (int i = 0; i < valueCount; i++) {
            node.values.add(buffer.getInt());
        }
        
        return node;
    }
    
    /**
     * 序列化节点到字节数组
     */
    public byte[] serialize() {
        ByteBuffer buffer = ByteBuffer.allocate(8192); // 8KB
        
        // 写入节点类型
        buffer.putInt(nodeType == NodeType.LEAF ? 0 : 1);
        
        // 写入是否是根节点
        buffer.putInt(isRoot ? 1 : 0);
        
        // 写入键的数量
        buffer.putInt(keys.size());
        
        // 写入下一个叶子节点ID
        buffer.putInt(nextLeafPageId);
        
        // 写入键
        for (Integer key : keys) {
            buffer.putInt(key);
        }
        
        // 写入值的数量
        buffer.putInt(values.size());
        
        // 写入值
        for (Integer value : values) {
            buffer.putInt(value);
        }
        
        return buffer.array();
    }
    
    /**
     * 检查节点是否已满
     */
    public boolean isFull() {
        return keys.size() >= order;
    }
    
    /**
     * 检查节点是否需要合并（键数过少）
     */
    public boolean needsMerge() {
        return keys.size() < order / 2;
    }
    
    /**
     * 在节点中查找键的插入位置
     */
    public int findInsertPosition(int key) {
        int pos = 0;
        while (pos < keys.size() && keys.get(pos) < key) {
            pos++;
        }
        return pos;
    }
    
    /**
     * 在叶子节点中插入键值对
     */
    public void insertInLeaf(int key, int value) {
        if (nodeType != NodeType.LEAF) {
            throw new IllegalStateException("只能在叶子节点插入键值对");
        }
        
        int pos = findInsertPosition(key);
        
        // 检查是否已存在相同的键
        if (pos < keys.size() && keys.get(pos) == key) {
            // 更新值
            values.set(pos, value);
        } else {
            // 插入新键值对
            keys.add(pos, key);
            values.add(pos, value);
        }
    }
    
    /**
     * 在内部节点中插入键和子节点ID
     */
    public void insertInInternal(int key, int childPageId) {
        if (nodeType != NodeType.INTERNAL) {
            throw new IllegalStateException("只能在内部节点插入");
        }
        
        int pos = findInsertPosition(key);
        keys.add(pos, key);
        // 注意：内部节点的values数量比keys多1
        // 新的子节点插入到pos+1位置
        values.add(pos + 1, childPageId);
    }
    
    /**
     * 分裂节点
     * @return 新创建的节点（右节点）和提升的键
     */
    public SplitResult split(int newPageId) {
        int mid = keys.size() / 2;
        
        BPlusTreeNode newNode = new BPlusTreeNode(newPageId, nodeType, order);
        
        if (nodeType == NodeType.LEAF) {
            // 叶子节点分裂：复制中间键到右节点
            int splitKey = keys.get(mid);
            
            // 右节点获得后半部分
            newNode.keys.addAll(keys.subList(mid, keys.size()));
            newNode.values.addAll(values.subList(mid, values.size()));
            
            // 左节点保留前半部分
            keys.subList(mid, keys.size()).clear();
            values.subList(mid, values.size()).clear();
            
            // 更新叶子节点链接
            newNode.nextLeafPageId = this.nextLeafPageId;
            this.nextLeafPageId = newPageId;
            
            return new SplitResult(newNode, splitKey);
        } else {
            // 内部节点分裂：提升中间键
            int splitKey = keys.get(mid);
            
            // 右节点获得后半部分（不包括中间键）
            newNode.keys.addAll(keys.subList(mid + 1, keys.size()));
            newNode.values.addAll(values.subList(mid + 1, values.size()));
            
            // 左节点保留前半部分
            keys.subList(mid, keys.size()).clear();
            values.subList(mid + 1, values.size()).clear();
            
            return new SplitResult(newNode, splitKey);
        }
    }
    
    // Getters and Setters
    
    public NodeType getNodeType() {
        return nodeType;
    }
    
    public int getPageId() {
        return pageId;
    }
    
    public boolean isRoot() {
        return isRoot;
    }
    
    public void setRoot(boolean isRoot) {
        this.isRoot = isRoot;
    }
    
    public List<Integer> getKeys() {
        return keys;
    }
    
    public List<Integer> getValues() {
        return values;
    }
    
    public int getNextLeafPageId() {
        return nextLeafPageId;
    }
    
    public void setNextLeafPageId(int nextLeafPageId) {
        this.nextLeafPageId = nextLeafPageId;
    }
    
    public int getOrder() {
        return order;
    }
    
    @Override
    public String toString() {
        return "BPlusTreeNode{" +
                "type=" + nodeType +
                ", pageId=" + pageId +
                ", isRoot=" + isRoot +
                ", keys=" + keys +
                ", values=" + values +
                ", nextLeaf=" + nextLeafPageId +
                '}';
    }
    
    /**
     * 分裂结果
     */
    public static class SplitResult {
        public final BPlusTreeNode newNode;
        public final int splitKey;
        
        public SplitResult(BPlusTreeNode newNode, int splitKey) {
            this.newNode = newNode;
            this.splitKey = splitKey;
        }
    }
}
